import ij.IJ;
import ij.ImagePlus;
import ij.gui.GenericDialog;
import ij.gui.Overlay;
import ij.gui.Roi;
import ij.gui.ShapeRoi;
import ij.plugin.filter.PlugInFilter;
import ij.process.FloatProcessor;
import ij.process.ImageProcessor;
import imagingbook.sift.SiftDescriptor;
import imagingbook.sift.SiftDetector;
import imagingbook.sift.util.Colors;

import java.awt.Color;
import java.awt.geom.Path2D;
import java.util.Collections;
import java.util.List;

/**
 * This plugin extracts multi-scale SIFT features from the current image and displays
 * them as M-shaped markers.
 * @author W. Burger
 * @version 2012/05/28
 */
public class Demo_ExtractSiftFeatures implements PlugInFilter {

	static double FEATURE_SCALE = 1.0; // 1.5;
	static double FEATURE_STROKEWIDTH = 1.0;
	static double DISPLAY_ANGLE_OFFSET = -Math.PI / 2;

	ImagePlus imp;
	Color[] colors = Colors.defaultDisplayColors;

	public int setup(String arg0, ImagePlus imp) {
		this.imp = imp;
		return DOES_8G + NO_CHANGES;
	}

	public void run(ImageProcessor ip) {
		FloatProcessor fp = (FloatProcessor) ip.convertToFloat();
		SiftDetector.Parameters params = new SiftDetector.Parameters();
		
		if (!setParameters(params)) {
			return;
		}
		
		SiftDetector sd = new SiftDetector(fp, params);
		
		IJ.log("Extracting SIFT features ...");
		List<SiftDescriptor> features = sd.getSiftFeatures();
		IJ.log("SIFT features found: " + features.size());
		
//		for (SiftDescriptor d : features) {
//			d.print();
//		}

		ImageProcessor ip2 = ip.duplicate();
		ImagePlus imp2 = new ImagePlus(imp.getShortTitle() + "-SIFT", ip2);
	
		Overlay oly = makeOverlayM(features);	// show key points (with orientation)

		if (oly != null) {
			imp2.setOverlay(oly);
			imp2.show();
		}
	}
	
	boolean setParameters(SiftDetector.Parameters params) {
			GenericDialog gd = new GenericDialog("Set SIFT parameters");
			gd.addNumericField("tMag :", params.t_Mag, 3, 6, "");
			gd.addNumericField("rMax :", params.reMax, 3, 6, "");
			gd.addNumericField("orientation histogram smoothing :", params.n_Smooth, 0, 6, "");
			gd.showDialog();
			if (gd.wasCanceled()) {
				return false;
			}
			params.t_Mag = gd.getNextNumber();
			params.reMax = gd.getNextNumber();
			params.n_Smooth = (int) gd.getNextNumber();
			params.DEBUG = false;
			return true;
	}

	
	/* shows the oriented keypoints marked "M"
	 */
	Overlay makeOverlayM(List<SiftDescriptor> fs) {
		Collections.reverse(fs);
		Overlay oly = new Overlay();
		for (SiftDescriptor f : fs) {
			double x = f.x; 
			double y = f.y; 
			double scale = FEATURE_SCALE * f.scale;
			double orient = f.orientation + DISPLAY_ANGLE_OFFSET;
			double sin = Math.sin(orient);
			double cos = Math.cos(orient);
			Path2D poly = new Path2D.Double();	
			poly.moveTo(x + (sin - cos) * scale, y - (sin + cos) * scale);
			poly.lineTo(x + (sin + cos) * scale, y + (sin - cos) * scale);
			poly.lineTo(x, y);
			poly.lineTo(x - (sin - cos) * scale, y + (sin + cos) * scale);
			poly.lineTo(x - (sin + cos) * scale, y - (sin - cos) * scale);
			poly.closePath();
			Roi shapeRoi = new ShapeRoi(poly);
			shapeRoi.setStrokeWidth((float)FEATURE_STROKEWIDTH);
			shapeRoi.setStrokeColor(colors[0]);
			oly.add(shapeRoi);
		}
		return oly;
	}
	
}
